package cz.via.facepalm.calendar.server;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.gson.Gson;

import cz.via.facepalm.shared.CActivity;
import cz.via.facepalm.shared.CUser;
import cz.via.facepalm.shared.CUserActivity;
import cz.via.facepalm.shared.ServerOperations;

public class JsonService implements Servlet {

	DatastoreService datastore;
	Gson gson = new Gson();

	@Override
	public void service(ServletRequest request, ServletResponse response)
			throws ServletException, IOException {
		HttpServletRequest req = (HttpServletRequest) request;
		HttpServletResponse res = (HttpServletResponse) response;

		ServerOperations op;
		try {
			op = ServerOperations.valueOf(req
					.getParameter(ServerOperations.OPERATION_PARAM));
		} catch (Exception e) {
			write("Specify valid operation", res);
			return;
		}
		switch (op) {
		case LoadUser:
			write(loadUser(req.getParameter("name"),req.getParameter("pass")), res);
			break;
		case CreateUser:
			createUser(gson.fromJson(
					req.getParameter(ServerOperations.ARG_PARAM), CUser.class));
			break;
		case LoadUserActivities:
			write(loadUserActivities(Long.valueOf(req.getParameter("cuser"))),
					res);
			break;
		case CreateActivity:
			createActivity(gson.fromJson(
					req.getParameter(ServerOperations.ARG_PARAM),
					CActivity.class));
			break;
		case ListActivities:
			write(listActivites(), res);
			break;
		case SubscribeToActivity:
			subscripeToActivity(Long.valueOf(req.getParameter("cuser")),
					Long.valueOf(req.getParameter("cactivity")));
			break;
		case UnsubscribeToActivity:
			unsubscripeToActivity(Long.valueOf(req.getParameter("cuser")),
					Long.valueOf(req.getParameter("cactivity")));
			break;
		case SaveOrChangeAttendance:
			saveOrChangeAttendance(gson.fromJson(
					req.getParameter(ServerOperations.ARG_PARAM),
					CUserActivity.class));
			break;
		case LoadUsersAttendanceForActivity:
			write(loadUsersAttendanceForActivity(Long.valueOf(req
					.getParameter("cactivity"))), res);
			break;
		case clearData:
			String parameter = req.getParameter("arg");
			if (parameter == null || parameter.isEmpty()) {
				deleteKind("cuser");
				deleteKind("cuseractivity");
				deleteKind("cactivity");
			} else {
				deleteKind(parameter);
			}
			break;
		default:
			break;
		}
	}

	private List<CUserActivity> loadUsersAttendanceForActivity(final Long idActivity) {
		return loadUserActivities(new DoneCallback<Query>() {
			@Override
			public void done(Query query) {
				query.addFilter("idActivity", FilterOperator.EQUAL, idActivity);
			}
		});
	}

	private void deleteKind(String kind) {
		PreparedQuery prepare = datastore.prepare(new Query(kind));
		Iterable<Entity> asIterable = prepare.asIterable();
		for (Entity entity : asIterable) {
			datastore.delete(entity.getKey());
		}
	}

	private CUser loadUser(String name,String pass) {
		Query query = new Query("cuser");
		query.addFilter("name", FilterOperator.EQUAL, name);
		PreparedQuery prepare = datastore.prepare(query);
		Iterator<Entity> iterator = prepare.asIterable().iterator();
		if (!iterator.hasNext())
			return null;
		Entity eUser = iterator.next();
		if(pass!=null){
			//le password check
			if(!pass.equals(eUser.getProperty("pass"))){
				return null;
			}
		}
		return userFromEntity(eUser);
	}

	private CUser userFromEntity(Entity eUser) {
		CUser cUser = new CUser();
		cUser.setId(eUser.getKey().getId());
		cUser.setName("" + eUser.getProperty("name"));
		return cUser;
	}

	private void createUser(CUser cuser) {
		CUser loadUser = loadUser(cuser.getName(),null);
		if (loadUser != null) {
			return;
		}
		Entity euser = new Entity("cuser");
		euser.setProperty("name", cuser.getName());
		euser.setProperty("pass", cuser.getPass());
		datastore.put(euser);
	}

	private void saveOrChangeAttendance(CUserActivity att) {
		try {
			Entity eAtt;
			eAtt = datastore.get(KeyFactory.createKey("cuseractivity",
					att.getId()));
			eAtt.setProperty("percentComming", att.getPercentComming());
			eAtt.setProperty("percentLate", att.getPercentLate());
			datastore.put(eAtt);
		} catch (EntityNotFoundException e) {
			e.printStackTrace();
			return;
		}
	}

	private void subscripeToActivity(Long idUser, Long idActivity) {
		Query query = new Query("cuseractivity");
		query.addFilter("idUser", FilterOperator.EQUAL, idUser);
		query.addFilter("idActivity", FilterOperator.EQUAL, idActivity);
		if (datastore.prepare(query).countEntities(
				FetchOptions.Builder.withDefaults()) > 0) {
			return;
		}

		Entity eAct = new Entity("cuseractivity");
		eAct.setProperty("idUser", idUser);
		eAct.setProperty("idActivity", idActivity);
		eAct.setProperty("percentComming", 100);
		eAct.setProperty("percentLate", 0);
		datastore.put(eAct);
	}
	
	private void unsubscripeToActivity(Long idUser, Long idActivity) {
		Query query = new Query("cuseractivity");
		query.addFilter("idUser", FilterOperator.EQUAL, idUser);
		query.addFilter("idActivity", FilterOperator.EQUAL, idActivity);
		Iterator<Entity> iter = datastore.prepare(query).asIterator(FetchOptions.Builder.withDefaults());
		if(iter.hasNext()){
			datastore.delete(iter.next().getKey());
		}
	}

	private List<CActivity> listActivites() {
		PreparedQuery prepare = datastore.prepare(new Query("cactivity"));
		List<Entity> asList = prepare.asList(FetchOptions.Builder
				.withDefaults());
		List<CActivity> ret = new ArrayList<CActivity>();
		for (Entity e : asList) {
			ret.add(activityFromEnity(e));
		}
		return ret;
	}

	private CActivity activityFromEnity(Entity e) {
		CActivity act = new CActivity();
		act.setId(e.getKey().getId());
		act.setName("" + e.getProperty("name"));
		act.setWeeklyTime("" + e.getProperty("weeklyTime"));
		return act;
	}

	private void createActivity(CActivity activity) {
		Entity eAct = new Entity("cactivity");
		eAct.setProperty("name", activity.getName());
		eAct.setProperty("weeklyTime", activity.getWeeklyTime());
		datastore.put(eAct);
	}

	private void write(Object obj, HttpServletResponse response) {
		try {
			response.getWriter().append(gson.toJson(obj));
			response.flushBuffer();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private List<CUserActivity> loadUserActivities(final Long idUser) {
		return loadUserActivities(new DoneCallback<Query>() {
			@Override
			public void done(Query query) {
				query.addFilter("idUser", FilterOperator.EQUAL, idUser);
			}
		});
	}

	private List<CUserActivity> loadUserActivities(DoneCallback<Query> filter) {
		try {
			List<CUserActivity> ret = new ArrayList<CUserActivity>();

			Query query = new Query("cuseractivity");
			filter.done(query);
			
			Iterable<Entity> asIterable = datastore.prepare(query).asIterable();
			for (Entity entity : asIterable) {
				CUserActivity userActivity = new CUserActivity();
				userActivity.setId(entity.getKey().getId());
				CActivity activity = activityFromEnity(datastore.get(KeyFactory
						.createKey("cactivity",
								intFromEntityProperty(entity, "idActivity"))));
				userActivity.setIdActivity(activity);
				userActivity.setIdUser(userFromEntity(datastore.get(KeyFactory
						.createKey("cuser",
								intFromEntityProperty(entity, "idUser")))));
				userActivity.setPercentComming(intFromEntityProperty(entity,
						"percentComming"));
				userActivity.setPercentLate(intFromEntityProperty(entity,
						"percentLate"));
				ret.add(userActivity);
			}
			return ret;
		} catch (EntityNotFoundException e) {
			return null;
		}
	}

	private Integer intFromEntityProperty(Entity entity, String property) {
		return Integer.valueOf("" + entity.getProperty(property));
	}

	@Override
	public void destroy() {
	}

	@Override
	public ServletConfig getServletConfig() {
		return null;
	}

	@Override
	public String getServletInfo() {
		return null;
	}

	@Override
	public void init(ServletConfig arg0) throws ServletException {
		datastore = DatastoreServiceFactory.getDatastoreService();
	}

}
